#pragma once

#include <string>
#include <fstream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#include <filesystem>
#include <zenox/unicode.hpp>
#include <zenox/minilog.hpp>

namespace zenox {

template <class Str = std::string>
inline Str file_get_content(std::string const &path) {
    std::ifstream fin(u8path(path));
    std::istreambuf_iterator<char> iit(fin), eiit;
    Str content;
    std::copy(iit, eiit, std::back_inserter(content));
    return content;
}

inline void file_put_content(std::string const &path, std::string const &content) {
    std::ofstream fout(u8path(path));
    fout << content;
}

inline bool file_exists(std::string const &path) {
    std::ifstream fin(u8path(path));
    return (bool)fin;
}

template <class Arr = std::vector<char>>
inline Arr file_get_binary(std::string const &path) {
    const char *filename = path.c_str();
    std::FILE *fp = zenox::u8fopen(filename, "rb");
    if (!fp) {
        zenox::fatal_error("{}: {}", path, std::strerror(errno));
        return {};
    }
    if (fseek(fp, 0, SEEK_END) != 0) {
        zenox::fatal_error("{}: {}", path, std::strerror(errno));
        std::fclose(fp);
        return {};
    }
    long size = std::ftell(fp);
    if (size == -1) {
        zenox::fatal_error("{}: {}", path, std::strerror(errno));
        std::fclose(fp);
        return {};
    }
    std::rewind(fp);
    Arr res;
    res.resize(size);
    size_t n = std::fread(res.data(), res.size(), 1, fp);
    if (n != 1) {
        zenox::fatal_error("{}: {}", path, std::strerror(errno));
        std::fclose(fp);
        return {};
    }
    std::fclose(fp);
    return res;
}

inline bool file_put_binary(std::string const &path, const void *arr_data, size_t arr_size) {
    const char *filename = path.c_str();
    std::FILE *fp = zenox::u8fopen(filename, "wb");
    if (!fp) {
        zenox::fatal_error("{}: {}", path, std::strerror(errno));
        return {};
    }
    size_t n = std::fwrite(arr_data, arr_size, 1, fp);
    if (n != 1) {
        zenox::fatal_error("{}: {}", path, std::strerror(errno));
    }
    std::fclose(fp);
    return true;
}

template <class Arr = std::vector<char>>
inline bool file_put_binary(std::string const &path, Arr const &arr) {
    return file_put_binary(std::data(arr), std::size(arr), path);
}

}
